from config.settings import get_settings
from get_logger import get_server_logger, get_error_logger
import redis
import time
from typing import Optional
import asyncio
from redis.asyncio import Redis, ConnectionPool


server_logger = get_server_logger()
error_logger = get_error_logger()

class RedisConnection:
    def __init__(self):
        self.settings = get_settings()
        self.redis_pool = redis.ConnectionPool(host=self.settings.REDIS_HOST, port=self.settings.REDIS_PORT, db=self.settings.REDIS_DB)
        self._redis_conn:redis.Redis = None
        self._exit = False
    
    def set_key(self, key, value):
        if not self._redis_conn:
            self.get_redis_connection()
        try:
            self._redis_conn.set(key, value)
        except redis.ConnectionError as e:
            server_logger.error(f"Redis set key error: {str(e)}")
            self.close_conn()
    
    def get_key(self, key):
        if not self._redis_conn:
            self.get_redis_connection()
        try:
            return self._redis_conn.get(key)
        except redis.ConnectionError as e:
            server_logger.error(f"Redis get key error: {str(e)}")
            self.close_conn()
    
    
    def get_redis_connection(self) -> redis.Redis:
        if self._redis_conn and self._redis_conn.ping():
            return self._redis_conn
            
        try:
            r = redis.Redis(connection_pool=self.redis_pool, 
                          socket_connect_timeout=0.5)
            if not r.ping():
                raise ConnectionError("Redis ping failed")
                
            server_logger.info("redis connection success")
            self._redis_conn = r
            return self._redis_conn
        except redis.ConnectionError as e:
            error_logger.error("redis connection error: %s", str(e))
            try:
                time.sleep(3)
            except KeyboardInterrupt:
                raise KeyboardInterrupt
            return self.get_redis_connection()  # 递归重试
        except Exception as e:
            self.close()
    
    def close_conn(self):
        if self._redis_conn:
            self._redis_conn.close()
            self._redis_conn = None
    
    def close_pool(self):
        if self.redis_pool:
            self.redis_pool.disconnect()
            self.redis_pool = None

    def close(self):
        self._exit = True
        self.close_conn()
        self.close_pool()


class AsyncRedisConnection:
    def __init__(self):
        self.settings = get_settings()
        self.redis_pool = ConnectionPool(
            host=self.settings.REDIS_HOST, 
            port=self.settings.REDIS_PORT, 
            db=self.settings.REDIS_DB
        )
        self._redis_conn: Optional[Redis] = None
        self.max_retries = 5
        
    async def async_get_redis_connection(self) -> Optional[Redis]:

        if self._redis_conn and await self._redis_conn.ping():
            return self._redis_conn

        retry_count = 0
        while retry_count < self.max_retries:
            try:
                r = Redis(connection_pool=self.redis_pool, 
                         socket_connect_timeout=0.5)
                if not await r.ping():
                    raise ConnectionError("Redis ping failed")
                
                server_logger.info("redis connection success")
                self._redis_conn = r
                return self._redis_conn
                
            except redis.ConnectionError as e:
                retry_count += 1
                error_logger.error(f"redis connection error: {str(e)}, retry {retry_count}/{self.max_retries}")
                if retry_count < self.max_retries:
                    await asyncio.sleep(3)
            except Exception as e:
                await self.close()
                raise

        return None

    async def async_set_key(self, key, value):
        try:
            await self._redis_conn.set(key, value)
        except redis.ConnectionError as e:
            server_logger.error(f"Redis set key error: {str(e)}")
            await self.close_conn()
            self._redis_conn = await self.async_get_redis_connection()

    async def async_get_key(self, key):
        try:
            return await self._redis_conn.get(key)
        except redis.ConnectionError as e:
            server_logger.error(f"Redis get key error: {str(e)}")
            await self.close_conn()
            self._redis_conn = await self.async_get_redis_connection()
        return "exit"
    
    async def close_conn(self):
        if self._redis_conn:
            await self._redis_conn.close()
            self._redis_conn = None
    
    async def close_pool(self):
        if self.redis_pool:
            await self.redis_pool.disconnect()
            self.redis_pool = None

    async def close(self):
        self._exit = True
        try:
            await self.close_conn()
            await self.close_pool()
        except asyncio.TimeoutError:
            server_logger.warning("Redis close timeout")
        except Exception as e:
            error_logger.error(f"Error closing redis: {str(e)}")

redis_connection = RedisConnection()
async_redis_connection = AsyncRedisConnection()